#pragma once

#include <iostream>
#include <queue>
#include <vector>
#include <pthread.h>
#include <functional>

using namespace std;

#include "LockGuard.hpp"
#include "Thread.hpp"
#include "log.hpp"

const int defaultNum = 5;

/**
 * ThreadPool 使用指南
 * ====================
 * 该线程池实现了一个基于模板的线程池类，允许用户使用任意类型的任务（需要定义任务的调用方式）。线程池支持单例模式，只允许创建一个实例。
 * 
 * 类功能概述：
 * -----------------
 * - **自动管理线程**：线程池在初始化时创建指定数量的线程。
 * - **任务队列支持**：用户可以将任务加入任务队列，线程池会自动分发任务到线程执行。
 * - **线程同步**：线程池通过条件变量和互斥锁进行线程同步，确保多线程操作安全。
 * 
 * 线程池主要接口：
 * -----------------
 * 1. **获取线程池实例**：
 *    `ThreadPool<T>::GetInstance()` 静态方法，获取线程池的唯一实例。
 * 2. **启动线程池**：
 *    `Start()` 方法启动所有线程，线程开始监听任务队列。
 * 3. **提交任务**：
 *    `Push(T task)` 方法将任务加入任务队列，线程池会自动唤醒线程处理任务。
 * 4. **等待线程退出**：
 *    `Wait()` 方法等待线程池中所有线程结束任务并退出。
 * 
 * 模板类型 T 的要求：
 * -----------------
 * - T 必须是可调用对象，必须重载 `()` 运算符或是函数对象。
 * - T 的对象通过 `Push(T task)` 加入任务队列。
 * - T 的调用方式：当任务被线程取出时，线程池通过 `task()` 的方式执行任务。
 * 
 * */


class ThreadData
{
public:
    ThreadData(const string &name): threadname(name)
    {}
    ~ThreadData()
    {}
public:
    string threadname;
};

template <class T>
class ThreadPool
{
private:
    ThreadPool(int threadNum = defaultNum) : _thread_num(threadNum)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);

        for (int i = 0; i < _thread_num; i++)
        {
            string name = "thread-";
            name += to_string(i + 1);
            ThreadData td(name);

            // 使该构造函数在执行回调函数时，绑定到ThreadRun上
            Thread<ThreadData> t(name,
                                 std::bind(&ThreadPool<T>::ThreadRun, this, std::placeholders::_1), td);
            _threads.push_back(t);
            lg.LogMessage(Info, "%s is created. \n", name.c_str());
        }
    }
    ThreadPool(const ThreadPool<T> &tp) = delete;
    const ThreadPool<T> &operator=(const ThreadPool<T>) = delete;
public:
    
    static ThreadPool<T> *GetInstance()
    {
        if(instance == nullptr)
        {
            LockGuard lockguard(&sig_lock);
            if(instance == nullptr)
            {
                lg.LogMessage(Info, "创建单例成功...\n");
                instance = new ThreadPool<T>();
            }
        }
        return instance;
    }

    bool Start()
    {
        // 启动线程
        for (auto &thread : _threads)
        {
            thread.Start();
            lg.LogMessage(Info, "%s is running. \n", thread.ThreadName().c_str());
        }
        return true;
    }
    void ThreadWait(ThreadData &td)
    {
        lg.LogMessage(Debug, "no task, %s is sleeping.\n", td.threadname.c_str());
        pthread_cond_wait(&_cond, &_mutex);
    }
    void ThreadWakeup()
    {
        pthread_cond_signal(&_cond);
    }
    // 隐含参数：this指针
    void ThreadRun(ThreadData &td)
    {
        while (true)
        {
            T t;
            // 访问临界资源进行加锁、解锁
            {
                LockGuard lockguard(&_mutex);
                // 当任务队列为空时，等待条件变量
                while (_q.empty())
                {
                    ThreadWait(td);
                }

                // 代码块外部已经声明了T, 重复声明导致传的数据变成乱码
                //T t = _q.front(); 
                t = _q.front();
                _q.pop(); 
            }
            // lg.LogMessage(Debug, "Thread %s executing task", td.threadname.c_str());
            t(); // 执行任务

        }
    }
    // 将任务放到任务队列中，并通知线程池唤醒线程
    void Push(T in)
    {
        LockGuard lockguard(&_mutex);
        _q.push(in);
        // lg.LogMessage(Debug, "Push task, queue size: %d", _q.size());
        ThreadWakeup();
    }

    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }

    void Wait()
    {
        for (auto &thread : _threads)
        {
            thread.Join();
        }
    }

private:
    queue<T> _q;
    vector<Thread<ThreadData>> _threads;
    int _thread_num;
    pthread_mutex_t _mutex;
    pthread_cond_t _cond;

    static ThreadPool<T> *instance;
    static pthread_mutex_t sig_lock;
};

template<class T>
ThreadPool<T> *ThreadPool<T>::instance = nullptr;
template<class T>
pthread_mutex_t ThreadPool<T>::sig_lock = PTHREAD_MUTEX_INITIALIZER;